/*
 * Copyright (C) 2008 Jive Software. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jivesoftware.openfire.archive;

import java.awt.Color;
import java.io.ByteArrayOutputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;

import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.NotFoundException;
import org.jivesoftware.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;

import com.lowagie.text.Chunk;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Font;
import com.lowagie.text.FontFactory;
import com.lowagie.text.Image;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfPageEventHelper;
import com.lowagie.text.pdf.PdfWriter;

/**
 * Utility class for asynchronous web calls for archiving tasks.
 *
 * @author Derek DeMoro
 */
public class ConversationUtils {

    private static final Logger Log = LoggerFactory.getLogger(ConversationUtils.class);


    public ByteArrayOutputStream buildPDFContent(Collection<Conversation> conversations, ArchiveSearch search)
    {
        Font roomEvent = FontFactory.getFont(FontFactory.HELVETICA, 12f, Font.ITALIC, new Color(0xFF, 0x00, 0xFF));

        try {
            Document document = new Document(PageSize.A4, 50, 50, 50, 50);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            PdfWriter writer = PdfWriter.getInstance(document, baos);
            writer.setPageEvent(new PDFEventListener());
            document.open();

            for (Conversation conversation : conversations)
            {
                if (JiveGlobals.getBooleanProperty("ofchat.pdf.ignore.room.event", true))
                {
                    boolean ignoreThis = true;

                    for (ArchivedMessage message : conversation.getMessages())
                    {
                        if (!message.isRoomEvent())
                        {
                           ignoreThis = false;
                        }
                    }

                    if (ignoreThis) continue;
                }

                Map<String, Font> colorMap = getColorMap(conversation);
                String title = search.getRoom() != null ? search.getRoom().getNode() : "";
                Paragraph p = new Paragraph(
                    LocaleUtils.getLocalizedString("archive.search.pdf.title", MonitoringConstants.NAME) + " " + title,
                    FontFactory.getFont(FontFactory.HELVETICA,
                        18, Font.BOLD));
                document.add(p);
                document.add(Chunk.NEWLINE);

                ConversationInfo coninfo = toConversationInfo(conversation, false);

                String participantsDetail;

                if (coninfo.getAllParticipants() == null) {
                    participantsDetail = coninfo.getParticipant1() + ", " + coninfo.getParticipant2();
                }
                else {
                    participantsDetail = String.valueOf(coninfo.getAllParticipants().length);
                }

                Paragraph chapterTitle = new Paragraph(
                    LocaleUtils
                        .getLocalizedString("archive.search.pdf.participants", MonitoringConstants.NAME) +
                        " " + participantsDetail,
                    FontFactory.getFont(FontFactory.HELVETICA, 12,
                        Font.BOLD));

                document.add(chapterTitle);


                Paragraph startDate = new Paragraph(
                    LocaleUtils.getLocalizedString("archive.search.pdf.startdate", MonitoringConstants.NAME) +
                        " " +
                        coninfo.getDate(),
                    FontFactory.getFont(FontFactory.HELVETICA, 12,
                        Font.BOLD));
                document.add(startDate);


                Paragraph duration = new Paragraph(
                    LocaleUtils.getLocalizedString("archive.search.pdf.duration", MonitoringConstants.NAME) +
                        " " +
                        coninfo.getDuration(),
                    FontFactory.getFont(FontFactory.HELVETICA, 12,
                        Font.BOLD));
                document.add(duration);

                Paragraph messageCount = new Paragraph(
                    LocaleUtils
                        .getLocalizedString("archive.search.pdf.messagecount", MonitoringConstants.NAME) +
                        " " +
                        conversation.getMessageCount(),
                    FontFactory.getFont(FontFactory.HELVETICA, 12,
                        Font.BOLD));
                document.add(messageCount);
                document.add(Chunk.NEWLINE);

                for (ArchivedMessage message : conversation.getMessages())
                {
                    Paragraph messageParagraph = null;

                    String time = JiveGlobals.formatTime(message.getSentDate());
                    String from = message.getFromJID().getNode();

                    if (conversation.getRoom() != null) {
                        from = message.getToJID().getResource();
                    }
                    String body = message.getBody();
                    String prefix;

                    if (!message.isRoomEvent()) {
                        prefix = "[" + time + "] " + from + ":  ";
                        Font font = colorMap.get(message.getFromJID().toString());
                        if (font == null) {
                            font = colorMap.get(message.getFromJID().toBareJID());
                        }
                        if (font == null) {
                            font = FontFactory.getFont(FontFactory.HELVETICA, 12f, Font.BOLD, Color.BLACK);
                        }
                        messageParagraph = new Paragraph(new Chunk(prefix, font));
                    }
                    else {

                        if (JiveGlobals.getBooleanProperty("ofchat.pdf.ignore.room.event", true) == false)
                        {
                            prefix = "[" + time + "] ";
                            messageParagraph = new Paragraph(new Chunk(prefix, roomEvent));
                        }
                    }

                    if (messageParagraph != null)
                    {
                        messageParagraph.add(body);
                        messageParagraph.add(" ");
                        document.add(messageParagraph);
                    }
                }
            }

            document.close();
            return baos;
        }
        catch (DocumentException e) {
            Log.error("error creating PDF document: " + e.getMessage(), e);
            return null;
        }
    }

    private Map<String, Font> getColorMap(Conversation conversation)
    {
        Font red = FontFactory
            .getFont(FontFactory.HELVETICA, 12f, Font.BOLD, new Color(0xFF, 0x00, 0x00));
        Font blue = FontFactory
            .getFont(FontFactory.HELVETICA, 12f, Font.ITALIC, new Color(0x00, 0x00, 0xFF));
        Font black = FontFactory.getFont(FontFactory.HELVETICA, 12f, Font.BOLD, Color.BLACK);

        Map<String, Font> colorMap = new HashMap<String, Font>();

        Collection<JID> set = conversation.getParticipants();
        int count = 0;
        for (JID jid : set) {
            if (conversation.getRoom() == null) {
                if (count == 0) {
                    colorMap.put(jid.toString(), blue);
                }
                else {
                    colorMap.put(jid.toString(), red);
                }
                count++;
            }
            else {
                colorMap.put(jid.toString(), black);
            }
        }
        return colorMap;
    }

    private ConversationInfo toConversationInfo(Conversation conversation, boolean formatParticipants)
    {
        final ConversationInfo info = new ConversationInfo();
        // Set participants
        Collection<JID> col = conversation.getParticipants();

        if (conversation.getRoom() == null) {
            JID user1 = (JID)col.toArray()[0];
            info.setParticipant1(formatJID(formatParticipants, user1));

            if (col.size() > 1)
            {
                JID user2 = (JID)col.toArray()[1];
                info.setParticipant2(formatJID(formatParticipants, user2));
            }
        }
        else {
            info.setConversationID(conversation.getConversationID());
            JID[] occupants = col.toArray(new JID[col.size()]);
            String[] jids = new String[col.size()];
            for (int i = 0; i < occupants.length; i++) {
                jids[i] = formatJID(formatParticipants, occupants[i]);
            }
            info.setAllParticipants(jids);
        }

        Map<String, String> cssLabels = new HashMap<String, String>();
        int count = 0;

        for (JID jid : col)
        {
            if (!cssLabels.containsKey(jid.toString()))
            {
                if (conversation.getRoom() == null) {
                    if (count % 2 == 0) {
                        cssLabels.put(jid.toBareJID(), "conversation-label2");
                    }
                    else {
                        cssLabels.put(jid.toBareJID(), "conversation-label1");
                    }
                    count++;
                }
                else {
                    cssLabels.put(jid.toString(), "conversation-label4");
                }
            }
        }

        // Set date
        info.setDate(JiveGlobals.formatDateTime(conversation.getStartDate()));
        info.setLastActivity(JiveGlobals.formatTime(conversation.getLastActivity()));
        // Create body.
        final StringBuilder builder = new StringBuilder();
        builder.append("<table width=100%>");

        for (ArchivedMessage message : conversation.getMessages())
        {
            String time = JiveGlobals.formatTime(message.getSentDate());
            String from = message.getFromJID().getNode();
            if (conversation.getRoom() != null) {
                from = message.getToJID().getResource();
            }
            from = StringUtils.escapeHTMLTags(from);
            String cssLabel = cssLabels.get(message.getFromJID().toBareJID());
            String body = StringUtils.escapeHTMLTags(message.getBody());
            builder.append("<tr valign=top>");
            if (!message.isRoomEvent()) {
                builder.append("<td width=1% nowrap class=" + cssLabel + ">").append("[")
                    .append(time).append("]").append("</td>");
                builder.append("<td width=1% class=" + cssLabel + ">").append(from).append(": ")
                    .append("</td>");
                builder.append("<td class=conversation-body>").append(body).append("</td");
            }
            else {
                builder.append("<td width=1% nowrap class=conversation-label3>").append("[")
                    .append(time).append("]").append("</td>");
                builder.append("<td colspan=2 class=conversation-label3><i>").append(body)
                    .append("</i></td");
            }
            builder.append("</tr>");
        }

        if (conversation.getMessages().size() == 0) {
            builder.append("<span class=small-description>" +
                LocaleUtils.getLocalizedString("archive.search.results.archive_disabled",
                        MonitoringConstants.NAME) +
                "</a>");
        }

        info.setBody(builder.toString());

        // Set message count
        info.setMessageCount(conversation.getMessageCount());

        long duration =
            (conversation.getLastActivity().getTime() - conversation.getStartDate().getTime());
        info.setDuration(duration);

        return info;
    }

    private String formatJID(boolean html, JID jid) {
        String formattedJID;
        if (html) {
            UserManager userManager = UserManager.getInstance();
            if (XMPPServer.getInstance().isLocal(jid) &&
                userManager.isRegisteredUser(jid, false)) {
                formattedJID = "<a href='/user-properties.jsp?username=" +
                    jid.getNode() + "'>" + jid.toBareJID() + "</a>";
            }
            else {
                formattedJID = jid.toBareJID();
            }
        }
        else {
            formattedJID = jid.toBareJID();
        }
        return formattedJID;
    }

    class PDFEventListener extends PdfPageEventHelper {

        @Override
        public void onEndPage(PdfWriter writer, Document document) {
            PdfContentByte cb = writer.getDirectContent();

            try {
                cb.setColorStroke(new Color(156, 156, 156));
                cb.setLineWidth(2);
                cb.moveTo(document.leftMargin(), document.bottomMargin() - 5);
                cb.lineTo(document.getPageSize().width() - document.rightMargin(),
                    document.bottomMargin() - 5);
                cb.stroke();

                ClassLoader classLoader = ConversationUtils.class.getClassLoader();
                Enumeration<URL> providerEnum = classLoader.getResources("images/pdf_generatedbyof.gif");
                while (providerEnum.hasMoreElements()) {
                    Image gif = Image.getInstance(providerEnum.nextElement());
                    cb.addImage(gif, 221, 0, 0, 28, (int)document.leftMargin(),
                        (int)document.bottomMargin() - 35);
                }

            }
            catch (Exception e) {
                Log.error("error drawing PDF footer: " + e.getMessage());
            }
            cb.saveState();

        }
    }
}
